#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/spi.h"

#include "wizchip_conf.h"
#include "bsp_spi.h"
#include "loopback.h"
#include "loopback.h" // Use loop test
#include "dhcp.h"     // Use dhcp
#include "socket.h"   // Use socket

#define SOCKET_ID 0                      // Socket number
#define ETHERNET_BUF_MAX_SIZE (1024 * 2) // Send and receive cache size
#define DHCP_RETRY_COUNT 5               // DHCP retry times

/**
 * @brief   Timer callback processing function, used for dhcp timing processing
 * @param   repeating :Timer structure
 * @return  bool
 */
bool repeating_timer_callback(struct repeating_timer *t);

/**
 * @brief   Initialization of chip network information
 * @param   conf_info :Static configuration information
 * @return  none
 */
void network_init(wiz_NetInfo *conf_info);

/**
 * @brief   TCP server establishes connection
 * @param   none
 * @return  none
 */
void do_tcp_server(void);

/* Network information to be configured. */
wiz_NetInfo net_info = {
    .mac = {0x00, 0x08, 0xdc, 0x11, 0x22, 0x33}, // Configured MAC address
    .ip = {192, 168, 1, 10},                     // Configured IP address
    .sn = {255, 255, 255, 0},                    // Configured subnet mask
    .gw = {192, 168, 1, 1},                      // Configured gateway
    .dns = {8, 8, 8, 8},                         // Configured domain address
    .dhcp = NETINFO_DHCP};                       // Configured dhcp model,NETINFO_DHCP:use dhcp; NETINFO_STATIC: use static ip.

wiz_NetInfo get_info;
static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {
    0,
};                                   // Send and receive cache
static uint16_t local_port = 8000;   // Local port
static uint8_t dhcp_get_ip_flag = 0; // Define the DHCP acquisition flag
static uint8_t buff[2048];

int main()
{
    struct repeating_timer timer; // Define the timer structure

    /* MCU init */
    stdio_init_all();              // Initialize the main control peripheral
    wizchip_initialize();          // Initialize the chip interface
    wizchip_setnetinfo(&net_info); // Configure once first

    /*dhcp init*/
    DHCP_init(SOCKET_ID, ethernet_buf);                                   // DHCP initialization
    add_repeating_timer_ms(1000, repeating_timer_callback, NULL, &timer); // Add DHCP 1s Tick Timer handler

    printf("wiznet chip tcp server example.\r\n");
    network_init(&net_info);              // Configuring Network Information
    print_network_information(&get_info); // Read back the configuration information and print it

    while (true)
    {
        do_tcp_server();
    }
}

void network_init(wiz_NetInfo *conf_info)
{
    int count = 0;
    int dhcp_state = 0;
    int breakout_flag = 0;

    if (conf_info->dhcp == NETINFO_DHCP)
    {
        while (true)
        {
            dhcp_state = DHCP_run(); // Do the DHCP client

            switch (dhcp_state)
            {
            case DHCP_IP_LEASED: // DHCP obtained the IP successfully
                if (dhcp_get_ip_flag == 0)
                {
                    dhcp_get_ip_flag = 1;
                    breakout_flag = 1;
                    getSHAR(get_info.mac);
                    getIPfromDHCP(get_info.ip);
                    getGWfromDHCP(get_info.gw);
                    getSNfromDHCP(get_info.sn);
                    getDNSfromDHCP(get_info.dns);
                    get_info.dhcp = NETINFO_DHCP;

                    /* Network initialize */
                    network_initialize(get_info); // apply from DHCP
                    printf("DHCP leased time : %ld seconds\n", getDHCPLeasetime());
                    close(SOCKET_ID);
                }
                break;
            case DHCP_FAILED: // DHCP failed to obtain IP
                count++;
                if (count <= DHCP_RETRY_COUNT) // If the number of times is less than or equal to the maximum number of times, try again
                {
                    printf("DHCP timeout occurred and retry %d \r\n", count);
                }
                else if (count > DHCP_RETRY_COUNT) // If the number of times is greater than DHCP fails
                {
                    breakout_flag = 1;
                    printf("DHCP failed \r\n");
                    DHCP_stop(); // Stop processing DHCP protocol

                    net_info.dhcp = NETINFO_STATIC;
                    wizchip_setnetinfo(&net_info); // Write configuration information
                }
            }
            if (breakout_flag == 1) // Get out of the loop
            {
                printf("config succ\r\n");
                break;
            }
        }
    }
    else
    {
        wizchip_setnetinfo(&net_info); // Configuring Network Information
    }
}

bool repeating_timer_callback(struct repeating_timer *t)
{
    DHCP_time_handler(); // DHCP 1s Tick Timer handler
    return true;
}

void do_tcp_server(void)
{
    static uint8_t sock_cof = 0;
    uint16_t len = 0;
    uint8_t destip[4];
    uint16_t destport;
    switch (getSn_SR(sock_cof)) /*Get the socket status*/
    {
    case SOCK_CLOSED:                                      /*The socket is closed*/
        socket(sock_cof, Sn_MR_TCP, local_port, Sn_MR_ND); // Open socket
        break;

    case SOCK_INIT:       /*The socket is in the initialization state*/
        listen(sock_cof); /*socket starts listening*/
        break;

    case SOCK_ESTABLISHED: /*The socket is in the connection establishment state*/

        if (getSn_IR(sock_cof) & Sn_IR_CON) /*Receive interrupt flags clearly*/
        {
            setSn_IR(sock_cof, Sn_IR_CON);
        }
        len = getSn_RX_RSR(sock_cof);
        if (len > 0)
        {
            recv(sock_cof, buff, len); /*print the receive data.*/
            buff[len] = 0x00;
            getSn_DIPR(sock_cof, destip);
            destport = getSn_DPORT(sock_cof);
            printf("socket%d form:%d.%d.%d.%d port: %d  message:%s\r\n", sock_cof, destip[0], destip[1], destip[2], destip[3], destport, buff);
            send(sock_cof, buff, len);
        }
        break;

    case SOCK_CLOSE_WAIT: /*The socket is waiting to be closed*/
        close(sock_cof);
        break;
    }
#if (_WIZCHIP_ == W5100S)
    if (sock_cof < 4)
    {
        sock_cof++;
    }
    else
    {
        sock_cof = 0;
    }
#elif (_WIZCHIP_ == W5500)
    if (sock_cof < 8)
    {
        sock_cof++;
    }
    else
    {
        sock_cof = 0;
    }
#endif
}